1 /*
2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021
3 License:   [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License].
4 Authors: Marcelo S. N. Mancini
5 
6 	Copyright Marcelo S. N. Mancini 2018 - 2021.
7 Distributed under the CC BY-4.0 License.
8    (See accompanying file LICENSE.txt or copy at
9 	https://creativecommons.org/licenses/by/4.0/
10 */
11 module hip.hiprenderer.renderer;
12 public import hip.hiprenderer.config;
13 public import hip.hiprenderer.shader;
14 public import hip.hiprenderer.vertex;
15 public import hip.hiprenderer.framebuffer;
16 public import hip.hiprenderer.viewport;
17 public import hip.api.renderer.texture;
18 public import hip.api.renderer.operations;
19 public import hip.api.graphics.color;
20 public import hip.hiprenderer.shader.shadervar;
21 import hip.windowing.window;
22 import hip.math.rect;
23 import hip.error.handler;
24 import hip.console.log;
25 
26 public import hip.hiprenderer.backend.gl.glrenderer;
27 
28 version(Windows)
29 {
30     import hip.hiprenderer.backend.d3d.d3drenderer;
31     import hip.hiprenderer.backend.d3d.d3dtexture;
32 }
33 version(AppleOS)
34 {
35     import hip.hiprenderer.backend.metal.mtlrenderer;
36     import hip.hiprenderer.backend.metal.mtltexture;
37 }
38 
39 version(dll){} else version = RendererConfigFile;
40 import hip.hiprenderer.backend.gl.gltexture;
41 
42 ///Could later be moved to windowing
43 enum HipWindowMode
44 {
45     WINDOWED,
46     FULLSCREEN,
47     BORDERLESS_FULLSCREEN
48 }
49 
50 ///Which API is being used
51 enum HipRendererType
52 {
53     GL3,
54     D3D11,
55     METAL,
56     NONE
57 }
58 
59 /// Primitive which the renderer will use
60 enum HipRendererMode
61 {
62     POINT,
63     LINE,
64     LINE_STRIP,
65     TRIANGLES,
66     TRIANGLE_STRIP
67 }
68 
69 
70 
71 
72 //////////////////////////////////////////Metadata//////////////////////////////////////////
73 
74 //Shaders
75 enum HipShaderInputLayout;
76 /**
77 *   Use this special UDA to say this type is only for accumulating stride and thus should not
78 *   be defined on shader
79 */
80 enum HipShaderInputPadding;
81 /**
82 *   Declares that the struct is as VertexUniform block. 
83 */
84 struct HipShaderVertexUniform
85 {
86     /**
87     *   This name is the base uniform name accessed when dealing with HLSL Api.
88     *   i.e: Constant Buffer block name
89     */
90     string name; 
91 }
92 /**
93 *   Declares that the struct is as FragmentUniform block. 
94 */
95 struct HipShaderFragmentUniform
96 {
97     /**
98     *   This name is the base uniform name accessed when dealing with HLSL Api.
99     *   i.e: Constant Buffer block name
100     */
101     string name;
102 }
103 
104 /**
105 *   Minimal interface for another API implementation
106 */
107 interface IHipRendererImpl
108 {
109     public bool init(HipWindow window);
110     version(dll){public bool initExternal();}
111     public bool isRowMajor();
112     void setErrorCheckingEnabled(bool enable = true);
113     public Shader createShader();
114     public ShaderVar* createShaderVar(ShaderTypes shaderType, UniformType uniformType, string varName, size_t length);
115     public IHipFrameBuffer createFrameBuffer(int width, int height);
116     public IHipVertexArrayImpl  createVertexArray();
117     public IHipVertexBufferImpl createVertexBuffer(size_t size, HipBufferUsage usage);
118     public IHipIndexBufferImpl  createIndexBuffer(index_t count, HipBufferUsage usage);
119     public IHipTexture  createTexture();
120     public int queryMaxSupportedPixelShaderTextures();
121     public void setColor(ubyte r = 255, ubyte g = 255, ubyte b = 255, ubyte a = 255);
122     public void setViewport(Viewport v);
123     public bool setWindowMode(HipWindowMode mode);
124     public void setDepthTestingEnabled(bool);
125     public void setDepthTestingFunction(HipDepthTestingFunction);
126     public void setStencilTestingEnabled(bool);
127     public void setStencilTestingMask(uint mask);
128     public void setColorMask(ubyte r, ubyte g, ubyte b, ubyte a);
129     ///When pass func evaluates to true, then it is said to be passed
130     public void setStencilTestingFunction(HipStencilTestingFunction passFunc, uint reference, uint mask);
131     public void setStencilOperation(HipStencilOperation stencilFail, HipStencilOperation depthFail, HipStencilOperation stencilAndDephPass);
132     public bool hasErrorOccurred(out string err, string line = __FILE__, size_t line =__LINE__);
133     public void begin();
134     public void setRendererMode(HipRendererMode mode);
135     public void drawIndexed(index_t count, uint offset = 0);
136     public void drawVertices(index_t count, uint offset = 0);
137     public void end();
138     public void clear();
139     public void clear(ubyte r = 255, ubyte g = 255, ubyte b = 255, ubyte a = 255);
140     public void dispose();
141 }
142 
143 private struct HipRendererResources
144 {
145     IHipTexture[] textures;
146     Shader[] shaders;
147     IHipVertexArrayImpl[]  vertexArrays;
148     IHipVertexBufferImpl[]  vertexBuffers;
149     IHipIndexBufferImpl[]  indexBuffers;
150 }
151 
152 class HipRenderer
153 {
154     static struct Statistics 
155     {
156         ulong drawCalls;
157         ulong renderFrames;
158     }
159     __gshared
160     {
161         protected Viewport currentViewport;
162         protected Viewport mainViewport;
163         protected IHipRendererImpl rendererImpl;
164         protected HipRendererMode rendererMode;
165         protected Statistics stats;
166         public  HipWindow window = null;
167         public  Shader currentShader;
168         package HipRendererType rendererType = HipRendererType.NONE;
169 
170         public uint width, height;
171         protected HipRendererConfig currentConfig;
172 
173         protected HipRendererResources res;
174         protected bool depthTestingEnabled;
175         protected HipDepthTestingFunction currentDepthTestFunction;
176     }
177 
178     version(RendererConfigFile)
179     public static bool initialize (string confData, string confPath)
180     {
181         import hip.data.ini;
182         IniFile ini = IniFile.parse(confData, confPath);
183         HipRendererConfig cfg;
184         if(ini.configFound && ini.noError)
185         {
186             cfg.bufferingCount = ini.tryGet!ubyte("buffering.count", 2);
187             cfg.multisamplingLevel = ini.tryGet!ubyte("multisampling.level", 0);
188             cfg.fullscreen = ini.tryGet("screen.fullscreen", false);
189             cfg.vsync = ini.tryGet("vsync.on", true);
190             
191             int width = ini.tryGet("screen.width", 1280);
192             int height = ini.tryGet("screen.height", 720);
193             string renderer = ini.tryGet("screen.renderer", "GL3");
194 
195             switch(renderer)
196             {
197                 case "GL3":
198                     version(OpenGL)
199                     {
200                         rendererType = HipRendererType.GL3;
201                         return initialize(new Hip_GL3Renderer(), &cfg, width, height);
202                     }
203                     else version(DirectX)
204                     {
205                         logln("OpenGL wasn't included in this build, using Direct3D");
206                         goto case "D3D11";
207                     }
208                     else version(AppleOS)
209                     {
210                         logln("OpenGL wasn't included in this build, using Metal");
211                         goto case "METAL";
212                     }
213                 case "D3D11":
214                     version(DirectX)
215                     {
216                         rendererType = HipRendererType.D3D11;
217                         return initialize(new Hip_D3D11_Renderer(), &cfg, width, height);
218                     }
219                     else version(OpenGL)
220                     {
221                         logln("Direct3D wasn't included in this build, using OpenGL 3");
222                         goto case "GL3";
223                     }
224                     else version(AppleOS)
225                     {
226                         logln("Direct3D wasn't included in this build, using Metal");
227                         goto case "METAL";
228                     }
229                 case "METAL":
230                     version(AppleOS)
231                     {
232                         rendererType = HipRendererType.METAL;
233                         return initialize(new HipMTLRenderer(), &cfg, width, height);
234                     }
235                     else version(OpenGL)
236                     {
237                         logln("Metal wasn't included in this build, using OpenGL 3");
238                         goto case "GL3";
239                     }
240                     else version(DirectX)
241                     {
242                         logln("Metal wasn't included in this build, using Direct3D");
243                         goto case "D3D11";
244                     }
245                 default:
246                     logln("Invalid renderer?" , renderer, " ' oh my freakin goodness");
247                     ErrorHandler.showErrorMessage("Invalid renderer '"~renderer~"'",
248                     `
249                         Available renderers:
250                             GL3
251                             D3D11
252                             METAL
253                         Starting with GL3
254                     `);
255                     goto case "GL3";
256             }
257         }
258         else
259         {
260             string defaultRenderer = "OpenGL3";
261             version(AppleOS) defaultRenderer = "Metal";
262             if(!ini.configFound)
263                 logln("No renderer.conf found");
264             if(!ini.noError)
265             {
266                 logln("Renderer.conf parsing error");
267                 rawerror(ini.errors);
268             }
269             hiplog("Defaulting renderer to "~defaultRenderer);
270         }
271         return initialize(getRendererFromVersion(rendererType), &cfg, 1280, 720);
272     }
273 
274     public static Statistics getStatistics(){return stats;}
275     private static IHipRendererImpl getRendererFromVersion(out HipRendererType type)
276     {
277         version(OpenGL)
278         {
279             type = HipRendererType.GL3;
280             return new Hip_GL3Renderer();
281         }
282         else version(DirectX)
283         {
284             type = HipRendererType.D3D11;
285             return new Hip_D3D11_Renderer();
286         }
287         else version(AppleOS)
288         {
289             type = HipRendererType.METAL;
290             return new HipMTLRenderer();
291         }
292         else
293         {
294             type = HipRendererType.NONE;
295             return null;
296         }
297     }
298     version(dll) private static IHipRendererImpl getRenderer(ref HipRendererType type)
299     {
300         final switch(type)
301         {
302             case HipRendererType.D3D11:
303                 version(DirectX) return new Hip_D3D11_Renderer();
304                             else return getRendererFromVersion(type);
305             case HipRendererType.GL3:
306                 version(OpenGL) return new Hip_GL3Renderer();
307                             else return getRendererFromVersion(type);
308             case HipRendererType.METAL:
309                 version(AppleOS) return new HipMTLRenderer();
310                             else return getRendererFromVersion(type);
311             case HipRendererType.NONE:
312                 return null;
313         }
314     }
315 
316     version(dll) public static bool initExternal(HipRendererType type, int windowWidth = -1, int windowHeight = -1)
317     {
318         rendererImpl = getRenderer(type);
319         HipRenderer.rendererType = type;
320         logln("Initialized renderer.: ", rendererType, " renderer? ", rendererImpl !is null);
321         bool ret = rendererImpl.initExternal();
322         if(!ret)
323             ErrorHandler.showErrorMessage("Error Initializing Renderer", "Renderer could not initialize externally");
324 
325         if(windowWidth == -1)
326             windowWidth = 1920;
327         if(windowHeight == -1)
328             windowHeight = 1080;
329 
330         window = createWindow(windowWidth, windowHeight);
331         HipRenderer.width = window.width;
332         HipRenderer.height = window.height;
333         afterInit();
334         return ret;
335     }
336     private static afterInit()
337     {
338         import hip.config.opts;
339         mainViewport = new Viewport(0,0, window.width, window.height);
340         setViewport(mainViewport);
341         setColor();
342         HipRenderer.setRendererMode(HipRendererMode.TRIANGLES);
343     }
344 
345     private static HipWindow createWindow(uint width, uint height)
346     {
347         HipWindow wnd = new HipWindow(width, height, HipWindowFlags.DEFAULT);
348         version(Android){}
349         else wnd.start();
350         return wnd;
351     }
352 
353     public static bool initialize (IHipRendererImpl impl, HipRendererConfig* config, uint width, uint height)
354     {
355         ErrorHandler.startListeningForErrors("Renderer initialization");
356         if(config != null)
357             currentConfig = *config;
358         currentConfig.logConfiguration();
359         rendererImpl = impl;
360         window = createWindow(width, height);
361         ErrorHandler.assertErrorMessage(window !is null, "Error creating window", "Could not create Window");
362         rendererImpl.init(window);
363         window.setVSyncActive(currentConfig.vsync);
364         window.setFullscreen(currentConfig.fullscreen);
365         window.show();
366         foreach(err; window.errors)
367             loglnError(err);
368         
369         setWindowSize(width, height);
370         afterInit();
371         return ErrorHandler.stopListeningForErrors();
372     }
373     public static void setWindowSize(int width, int height)
374     {
375         assert(width > 0 && height > 0, "Window width and height must be greater than 0");
376         logln("Changing window size to ", [width, height]);
377         window.setSize(cast(uint)width, cast(uint)height);
378         HipRenderer.width  = width;
379         HipRenderer.height = height;
380     }
381     public static HipRendererType getRendererType(){return rendererType;}
382     public static HipRendererConfig getCurrentConfig(){return currentConfig;}
383     public static int getMaxSupportedShaderTextures(){return rendererImpl.queryMaxSupportedPixelShaderTextures();}
384 
385 
386     private static IHipTexture _getTextureImplementation()
387     {
388         return rendererImpl.createTexture();
389     }
390     public static IHipTexture getTextureImplementation()
391     {
392         res.textures~= _getTextureImplementation();
393         return res.textures[$-1];
394     }
395 
396     public static void setColor(ubyte r = 255, ubyte g = 255, ubyte b = 255, ubyte a = 255)
397     {
398         rendererImpl.setColor(r,g,b,a);
399     }
400 
401     public static Viewport getCurrentViewport(){return currentViewport;}
402     public static void setViewport(Viewport v)
403     {
404         this.currentViewport = v;
405         v.updateForWindowSize(width, height);
406         rendererImpl.setViewport(v);
407     }
408 
409     public static void reinitialize()
410     {
411         version(Android)
412         {
413             foreach(tex; res.textures)
414             {
415                 (cast(Hip_GL3_Texture)tex).reload();
416             }
417             foreach(shader; res.shaders)
418             {
419                 shader.reload();
420             }
421         }
422     }
423 
424     public static void setCamera()
425     {
426         
427     }
428     /**
429     * Fixes the matrix order based on the config and renderer.
430     * If the renderer is column and the config is row, it will tranpose
431     */
432     public static T getMatrix(T)(ref T mat)
433     {
434         if(currentConfig.isMatrixRowMajor && !rendererImpl.isRowMajor())
435             return mat.transpose();
436         return mat;
437     }
438 
439     private static Shader _createShader()
440     {
441         res.shaders~= rendererImpl.createShader();
442         return res.shaders[$-1];
443     }
444     public static ShaderVar* createShaderVar(ShaderTypes shaderType, UniformType uniformType, string varName, size_t length)
445     {
446         return rendererImpl.createShaderVar(shaderType, uniformType, varName, length);
447     }
448 
449 
450     public static Shader newShader(HipShaderPresets shaderPreset = HipShaderPresets.DEFAULT)
451     {
452         Shader ret = _createShader();
453         ret.setFromPreset(shaderPreset);
454         return ret;
455     }
456     public static Shader newShader(string vertexShader, string fragmentShader)
457     {
458         Shader ret = _createShader();
459         ret.loadShadersFromFiles(vertexShader, fragmentShader);
460         return ret;
461     }
462     public static HipFrameBuffer newFrameBuffer(int width, int height, Shader frameBufferShader = null)
463     {
464         return new HipFrameBuffer(rendererImpl.createFrameBuffer(width, height), width, height, frameBufferShader);
465     }
466     public static IHipVertexArrayImpl  createVertexArray()
467     {
468         res.vertexArrays~= rendererImpl.createVertexArray();
469         return res.vertexArrays[$-1];
470     }
471     public static IHipVertexBufferImpl  createVertexBuffer(size_t size, HipBufferUsage usage)
472     {
473         res.vertexBuffers~= rendererImpl.createVertexBuffer(size, usage);
474         return res.vertexBuffers[$-1];
475     }
476     public static IHipIndexBufferImpl createIndexBuffer(index_t count, HipBufferUsage usage)
477     {
478         res.indexBuffers~= rendererImpl.createIndexBuffer(count, usage);
479         return res.indexBuffers[$-1];
480     }
481     public static void setShader(Shader s)
482     {
483         currentShader = s;
484         s.bind();
485     }
486     public static bool hasErrorOccurred(out string err, string file = __FILE__, size_t line =__LINE__)
487     {
488         return rendererImpl.hasErrorOccurred(err, file, line);
489     }
490 
491     public static void exitOnError(string file = __FILE__, size_t line = __LINE__)
492     {
493         import core.stdc.stdlib:exit;
494         string err;
495         if(hasErrorOccurred(err, file, line))
496         {
497             loglnError(err, file,":",line);
498             exit(-1);
499         }
500     }
501 
502     public static void begin()
503     {
504 
505         rendererImpl.begin();
506     }
507     
508     public static void setErrorCheckingEnabled(bool enable = true)
509     {
510         rendererImpl.setErrorCheckingEnabled(enable);
511     }
512 
513     public static void setRendererMode(HipRendererMode mode)
514     {
515         rendererMode = mode;
516         rendererImpl.setRendererMode(mode);
517     }
518     public static HipRendererMode getMode(){return rendererMode;}
519 
520     public static void drawIndexed(index_t count, uint offset = 0)
521     {
522         rendererImpl.drawIndexed(count, offset);
523         stats.drawCalls++;
524     }
525     public static void drawIndexed(HipRendererMode mode, index_t count, uint offset = 0)
526     {
527         HipRendererMode currMode = rendererMode;
528         if(mode != currMode) HipRenderer.setRendererMode(mode);
529         HipRenderer.drawIndexed(count, offset);
530         stats.drawCalls++;
531         if(mode != currMode) HipRenderer.setRendererMode(currMode);
532     }
533     public static void drawVertices(index_t count, uint offset = 0)
534     {
535         rendererImpl.drawVertices(count, offset);
536     }
537     public static void drawVertices(HipRendererMode mode, index_t count, uint offset = 0)
538     {
539         rendererImpl.setRendererMode(mode);
540         HipRenderer.drawVertices(count, offset);
541     }
542 
543     public static void end()
544     {
545         rendererImpl.end();
546         foreach(sh; res.shaders) sh.onRenderFrameEnd();
547         stats.drawCalls=0;
548         stats.renderFrames++;
549     }
550     public static void clear()
551     {
552         rendererImpl.clear();
553         stats.drawCalls++;
554     }
555     public static void clear(HipColorf color)
556     {
557         auto rgba = color.unpackRGBA;
558         rendererImpl.clear(rgba[0], rgba[1], rgba[2], rgba[3]);
559         stats.drawCalls++;
560     }
561     public static void clear(ubyte r = 255, ubyte g = 255, ubyte b = 255, ubyte a = 255)
562     {
563         rendererImpl.clear(r,g,b,a);
564         stats.drawCalls++;
565     }
566     static HipDepthTestingFunction getDepthTestingFunction()
567     {
568         return currentDepthTestFunction;
569     }
570     static bool isDepthTestingEnabled()
571     {
572         return depthTestingEnabled;
573     }
574     static void setDepthTestingEnabled(bool enable)
575     {
576         rendererImpl.setDepthTestingEnabled(enable);
577     }
578     static void setDepthTestingFunction(HipDepthTestingFunction fn)
579     {
580         rendererImpl.setDepthTestingFunction(fn);
581         currentDepthTestFunction = fn;
582     }
583 
584     static void setStencilTestingEnabled(bool bEnable)
585     {
586         rendererImpl.setStencilTestingEnabled(bEnable);
587     }
588     static void setStencilTestingMask(uint mask)
589     {
590         rendererImpl.setStencilTestingMask(mask);
591     }
592     static void setColorMask(ubyte r, ubyte g, ubyte b, ubyte a)
593     {
594         rendererImpl.setColorMask(r,g,b,a);
595     }
596     static void setStencilTestingFunction(HipStencilTestingFunction passFunc, uint reference, uint mask)
597     {
598         rendererImpl.setStencilTestingFunction(passFunc, reference, mask);
599     }
600     static void setStencilOperation(HipStencilOperation stencilFail, HipStencilOperation depthFail, HipStencilOperation stencilAndDephPass)
601     {
602         rendererImpl.setStencilOperation(stencilFail, depthFail, stencilAndDephPass);
603     }
604     
605     public static void dispose()
606     {
607         rendererImpl.dispose();
608         if(window !is null)
609             window.exit();
610         window = null;
611     }
612 }